import { _decorator, Component, EventTouch, Input, input, instantiate, Node, Prefab, SpriteFrame, UITransform, Vec2 } from 'cc'
import DataManager from '../Global/DataManager'
import { ResourceManager } from '../Global/ResourceManager'
import { ActorManager } from '../Entity/Actor/ActorManager'
import { EventEnum, PrefabPathEnum, TexturePathEnum } from '../Enum'
import { ApiMsgEnum, EntityTypeEnum, IClientInput, InputTypeEnum } from '../Common'
import { BulletManager } from '../Entity/Bullet/BulletManager'
import ObjectPoolManager from '../Global/ObjectPoolManager'
import NetworkManager from '../Global/NetworkManager'
import EventManager from '../Global/EventManager'
import { IMsgClientSync, IMsgServerSync } from '../Common/Msg'
import { deepClone } from '../Utils'
const { ccclass, property } = _decorator

@ccclass('BattleManager')
export class BattleManager extends Component {
    private stage: Node
    private ui: Node

    private pendingMsg: IMsgClientSync[] = []

    private shouldUpdate = false // 所有资源加载完才允许 update

    onLoad() { }

    async start() {
        this.clearGame()
        await Promise.all([this.connectServer(), this.loadRes()])
        this.initGame()
    }

    initGame() {
        DataManager.Instance.jm = this.ui.getComponentInChildren("JoyStickManager") as any
        this.initMap()
        this.shouldUpdate = true

        EventManager.Instance.on(EventEnum.ClientSync, this.handleClientSync, this)
        NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgServerSync, this.handleServerSync, this)
    }

    clearGame() {
        EventManager.Instance.off(EventEnum.ClientSync, this.handleClientSync, this)
        NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgServerSync, this.handleServerSync, this)
        DataManager.Instance.stage = this.stage = this.node.getChildByName("Stage")
        this.ui = this.node.getChildByName("UI")
        this.stage.destroyAllChildren() // 清空舞台上的所有节点
    }

    async connectServer() {
        if (!(await NetworkManager.Instance.connect().catch(() => false))) {
            await new Promise(rs => setTimeout(rs, 1000)) // 连接失败，等待1秒后重试
            await this.connectServer() // 递归重试连接
        }
    }

    async loadRes() {
        const list = []
        for (const type in PrefabPathEnum) {
            const p = ResourceManager.Instance.loadRes(PrefabPathEnum[type], Prefab).then(prefab => {
                DataManager.Instance.prefabMap.set(type, prefab)
            })
            list.push(p)
        }

        for (const type in TexturePathEnum) {
            const p = ResourceManager.Instance.loadDir(TexturePathEnum[type], SpriteFrame).then(spriteFrames => {
                DataManager.Instance.textureMap.set(type, spriteFrames)
            })
            list.push(p)
        }
        await Promise.all(list)
    }

    initMap() {
        const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Map)
        const map = instantiate(prefab)
        map.setParent(this.stage)
    }

    update(dt) {
        if (!this.shouldUpdate) {
            return
        }
        this.render()
        this.tick(dt)
    }

    tick(dt) {
        this.tickActor(dt)

        // 时间流逝应当交给服务端，不然每个客户端都流逝就不准了
        // DataManager.Instance.applyInput({
        //     type: InputTypeEnum.TimePast,
        //     dt,
        // })
    }

    tickActor(dt) {
        for (const data of DataManager.Instance.state.actors) {
            const { id } = data
            const am = DataManager.Instance.actorMap.get(id)
            am.tick(dt)
        }
    }

    render() {
        this.renderActor()
        this.renderBullet()
    }

    renderActor() {
        for (const data of DataManager.Instance.state.actors) {
            const { id, type } = data
            let am = DataManager.Instance.actorMap.get(id)
            if (!am) {
                const prefab = DataManager.Instance.prefabMap.get(type)
                const actor = instantiate(prefab)
                actor.setParent(this.stage)
                am = actor.addComponent(ActorManager)
                DataManager.Instance.actorMap.set(id, am)
                am.init(data)
            } else {
                am.render(data)
            }
        }
    }

    renderBullet() {
        for (const data of DataManager.Instance.state.bullets) {
            const { id, type } = data
            let bm = DataManager.Instance.bulletMap.get(id)
            if (!bm) {
                // const prefab = DataManager.Instance.prefabMap.get(type)
                // const bullet = instantiate(prefab)
                // bullet.setParent(this.stage)
                const bullet = ObjectPoolManager.Instance.get(type)
                // 从对象池取出的可能已经有 BulletManager 组件了
                bm = bullet.getComponent(BulletManager) || bullet.addComponent(BulletManager)
                DataManager.Instance.bulletMap.set(id, bm)
                bm.init(data)
            } else {
                bm.render(data)
            }
        }
    }

    handleClientSync(input: IClientInput) {
        const msg: IMsgClientSync = {
            input,
            frameId: DataManager.Instance.frameId++,
        }
        NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, msg)

        // input 应用到本地作为预测输入
        if(input.type === InputTypeEnum.ActorMove) {
            DataManager.Instance.applyInput(input)
            this.pendingMsg.push(msg)
        }
    }

    handleServerSync({ inputs, lastFrameId }: IMsgServerSync) {
        // 回滚到上一次服务器状态
        DataManager.Instance.state = DataManager.Instance.lastState
        
        // 应用服务器输入
        for (const input of inputs) {
            DataManager.Instance.applyInput(input)
        }

         // 记录服务器状态
        DataManager.Instance.lastState = deepClone(DataManager.Instance.state)

         // 应用本地输入
        this.pendingMsg = this.pendingMsg.filter((msg)=>msg.frameId > lastFrameId)
        for (const msg of this.pendingMsg) {
            DataManager.Instance.applyInput(msg.input)
        }
    }
}
